
#include <winbase.h>
#include <process.h>
#include "process-internal.h"

unsigned long __tlsindex = 0xffffffff;

unsigned long WINAPI _threadstart (void * ptd);
unsigned long WINAPI _threadstartex(void * ptd);

void _ptd_init(_ptiddata ptd);
void _ptd_free(_ptiddata ptd);

void _init_multithread()
{
	if((__tlsindex = TlsAlloc()) == 0xFFFFFFFF)
		xceabort();
}

unsigned long beginthread(void (*initialcode)(void *), unsigned stacksize, void * argument)
{
	_ptiddata ptd;
	unsigned long thdl;
	unsigned long errcode = 0L;

	if(__tlsindex == 0xFFFFFFFF)
		_init_multithread();

	if ( (ptd = (void *)calloc(1, sizeof(struct _tiddata))) == NULL )
		goto error_return;

	_ptd_init(ptd);

	ptd->_initaddr = (void *)initialcode;
	ptd->_initarg = argument;

	thdl = (unsigned long)CreateThread(NULL, stacksize, _threadstart, (LPVOID)ptd, CREATE_SUSPENDED, (LPDWORD)(&(ptd->_tid)));
	if (thdl == 0L) {
		errcode = GetLastError();
		goto error_return;
	}
	ptd->_thandle = thdl;

	if (ResumeThread( (HANDLE)thdl ) == (DWORD)(-1L)) {
		errcode = GetLastError();
		goto error_return;
	}

	return(thdl);

error_return:
	free(ptd);
	return((unsigned long)-1L);
}

void endthread(void)
{
	_ptiddata ptd;

	if((ptd = _ptd_get()) == NULL )
		abort();

	if(ptd->_thandle != (unsigned long)(-1L))
		CloseHandle((HANDLE)(ptd->_thandle));

	_ptd_free(ptd);

	ExitThread(0);
}

unsigned long beginthreadex(void *security, unsigned stacksize,
	unsigned (*initialcode)(void *), void * argument, unsigned createflag, unsigned *thrdaddr)
{
	_ptiddata ptd; 
	unsigned long thdl;
	unsigned long errcode = 0L;

	if((ptd = calloc(1, sizeof(struct _tiddata))) == NULL)
		goto error_return;

	_ptd_init(ptd);

	ptd->_initaddr = (void *)initialcode;
	ptd->_initarg = argument;
	ptd->_thandle = (unsigned long)(-1L);

	thdl = (unsigned long)CreateThread(security, stacksize, _threadstartex, (LPVOID)ptd, createflag, thrdaddr);
	if (thdl == 0L) {
		errcode = GetLastError();
		goto error_return;
	}

	return(thdl);

error_return:
	free(ptd);

	return((unsigned long)0L);
}

void endthreadex (unsigned retcode)
{
	_ptiddata ptd;

	if((ptd = _ptd_get()) == NULL)
		abort();

	_ptd_free(ptd);

	ExitThread(retcode);
}

void _ptd_init(_ptiddata ptd)
{
	ptd->_opterr = 1;
	ptd->_optind = 1;
}

void _ptd_free(_ptiddata ptd)
{
	if (__tlsindex != 0xFFFFFFFF) {
		if (!ptd) {
			ptd = TlsGetValue(__tlsindex);
		}

		if(ptd) {
			free((void *)ptd);
		}

		TlsSetValue(__tlsindex, (LPVOID)0);
	}
}

unsigned long WINAPI _threadstart (void * ptd)
{
	if(!TlsSetValue(__tlsindex, ptd))
		abort();

	((void(__cdecl *)(void *))(((_ptiddata)ptd)->_initaddr))
		(((_ptiddata)ptd)->_initarg );

	endthread();

	// not reached
	return(0L);
}

unsigned long WINAPI _threadstartex(void * ptd)
{
	if(!TlsSetValue(__tlsindex, ptd))
		abort();

	((_ptiddata) ptd)->_tid = GetCurrentThreadId();

	endthreadex(((unsigned (WINAPI *)(void *))(((_ptiddata)ptd)->_initaddr))
		(((_ptiddata)ptd)->_initarg));

	// not reached
	return(0L);
}

_ptiddata _ptd_get (void)
{
	_ptiddata ptd;
	DWORD TL_LastError;

	TL_LastError = GetLastError();

	if ((ptd = TlsGetValue(__tlsindex)) == NULL) 
	{
		if(((ptd = (void*)calloc(1, sizeof(struct _tiddata))) != NULL) &&
			TlsSetValue(__tlsindex, (LPVOID)ptd)) 
		{
			_ptd_init(ptd);

			ptd->_tid = GetCurrentThreadId();
			ptd->_thandle = (unsigned long)(-1L);
		}
		else
		{
			xceabort();
		}
	}

	SetLastError(TL_LastError);

	return(ptd);
}
